package sound;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class SoundRecorder {
    private static int RECORDER_SAMPLERATE = 16000;
    private static final int RECORDER_BPP = 16;
    private static int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
    private static int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    private static int TIME_OUT = 50;
    private static int SILENT_LIMIT = 3000;
    private int bufferSizeInBytes;
    private AudioRecord audioRecorder;
    private Handler messageHandler; 
    private boolean isStopped;
    private boolean isCancelled;
    
	public SoundRecorder(Context context, Handler messageHandler) {
        this.messageHandler = messageHandler;
        AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
        audioManager.setMode(AudioManager.MODE_NORMAL);

        bufferSizeInBytes = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
                                                         RECORDER_CHANNELS,
                                                         RECORDER_AUDIO_ENCODING); 
	}
	
	private void sendMessage(String message)
	{
		Message msg = new Message();
		msg.obj = message;
		messageHandler.sendMessage(msg);
	}
	public void start() {
        audioRecorder = new AudioRecord( MediaRecorder.AudioSource.DEFAULT,
                                         RECORDER_SAMPLERATE,
                                         RECORDER_CHANNELS,
                                         RECORDER_AUDIO_ENCODING,
                                         bufferSizeInBytes
                                        );
        audioRecorder.startRecording();

        int numberOfReadBytes   = 0; 
        byte audioBuffer[]      = new  byte[bufferSizeInBytes];
        boolean recording       = false;
        float tempFloatBuffer[] = new float[3];
        int tempIndex           = 0;
        int totalReadBytes      = 0;
        byte totalByteBuffer[]  = new byte[60 * 44100 * 2];
        int timer 				= 0;
        isStopped = false;
        isCancelled = false;

        while( true )
        {
            float totalAbsValue = 0.0f;
            short sample        = 0; 

            numberOfReadBytes = audioRecorder.read( audioBuffer, 0, bufferSizeInBytes );

            for( int i=0; i<bufferSizeInBytes; i+=2 ) 
            {
                sample = (short)( (audioBuffer[i]) | audioBuffer[i + 1] << 8 );
                totalAbsValue += Math.abs( sample ) / (numberOfReadBytes/2);
            }
            tempFloatBuffer[tempIndex%3] = totalAbsValue;
            float temp                   = 0.0f;
            for ( int i=0; i<3; ++i )
                temp += tempFloatBuffer[i];

            if( temp > SILENT_LIMIT && recording == false )
            {
                Log.i("TAG", "2");
                recording = true;
                sendMessage("Recording...");
                timer = 0;
            }
            else  if (temp > SILENT_LIMIT) timer = 0;

            if(((temp >= 0 && temp <= SILENT_LIMIT) && recording == true && timer > TIME_OUT ) || isStopped)
            {
                if (isCancelled) {
                	Log.i("TAG", "Cancel recording.");
                	audioRecorder.stop();
                    audioRecorder.release();
                    return;
                }
                
                Log.i("TAG", "Save audio to file.");
                
                long totalAudioLen  = 0;
                long totalDataLen   = totalAudioLen + 36;
                long longSampleRate = RECORDER_SAMPLERATE;
                int channels        = 1;
                long byteRate       = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8;
                totalAudioLen       = totalReadBytes;
                totalDataLen        = totalAudioLen + 36;
                byte finalBuffer[]  = new byte[totalReadBytes + 44];

                finalBuffer[0] = 'R'; 
                finalBuffer[1] = 'I';
                finalBuffer[2] = 'F';
                finalBuffer[3] = 'F';
                finalBuffer[4] = (byte) (totalDataLen & 0xff);
                finalBuffer[5] = (byte) ((totalDataLen >> 8) & 0xff);
                finalBuffer[6] = (byte) ((totalDataLen >> 16) & 0xff);
                finalBuffer[7] = (byte) ((totalDataLen >> 24) & 0xff);
                finalBuffer[8] = 'W';
                finalBuffer[9] = 'A';
                finalBuffer[10] = 'V';
                finalBuffer[11] = 'E';
                finalBuffer[12] = 'f';  
                finalBuffer[13] = 'm';
                finalBuffer[14] = 't';
                finalBuffer[15] = ' ';
                finalBuffer[16] = 16; 
                finalBuffer[17] = 0;
                finalBuffer[18] = 0;
                finalBuffer[19] = 0;
                finalBuffer[20] = 1;  
                finalBuffer[21] = 0;
                finalBuffer[22] = (byte) channels;
                finalBuffer[23] = 0;
                finalBuffer[24] = (byte) (longSampleRate & 0xff);
                finalBuffer[25] = (byte) ((longSampleRate >> 8) & 0xff);
                finalBuffer[26] = (byte) ((longSampleRate >> 16) & 0xff);
                finalBuffer[27] = (byte) ((longSampleRate >> 24) & 0xff);
                finalBuffer[28] = (byte) (byteRate & 0xff);
                finalBuffer[29] = (byte) ((byteRate >> 8) & 0xff);
                finalBuffer[30] = (byte) ((byteRate >> 16) & 0xff);
                finalBuffer[31] = (byte) ((byteRate >> 24) & 0xff);
                finalBuffer[32] = (byte) (2 * 16 / 8);  
                finalBuffer[33] = 0;
                finalBuffer[34] = RECORDER_BPP; 
                finalBuffer[35] = 0;
                finalBuffer[36] = 'd';
                finalBuffer[37] = 'a';
                finalBuffer[38] = 't';
                finalBuffer[39] = 'a';
                finalBuffer[40] = (byte) (totalAudioLen & 0xff);
                finalBuffer[41] = (byte) ((totalAudioLen >> 8) & 0xff);
                finalBuffer[42] = (byte) ((totalAudioLen >> 16) & 0xff);
                finalBuffer[43] = (byte) ((totalAudioLen >> 24) & 0xff);

                for( int i=0; i<totalReadBytes; ++i )
                    finalBuffer[44+i] = totalByteBuffer[i];

                File myDir = new File(Environment.getExternalStorageDirectory().toString() + "/Records");
                myDir.mkdirs();
                File file = new File (myDir, "sample.wav");
                if (file.exists()) file.delete();

                try {
                	FileOutputStream out = new FileOutputStream(file);
                     try {
                            out.write(finalBuffer);
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                } catch (FileNotFoundException e1) {
                    e1.printStackTrace();
                }

                tempIndex++;
                break;
            }

            Log.i( "TAG", "Recording Sound." );
            for( int i=0; i<numberOfReadBytes; i++ )
                totalByteBuffer[totalReadBytes + i] = audioBuffer[i];
            totalReadBytes += numberOfReadBytes;

            tempIndex++;
            timer++;
            if (timer>1) sendMessage("No sound detected. Stop recording in "+ (TIME_OUT-timer)+ "...");
            else sendMessage("Recording...");
            Log.i("TAG",Integer.toString(timer));
        }
        audioRecorder.stop();
        audioRecorder.release();
	}
	
	public void stop() {
		isStopped = true;
	}
	
	public void cancel() {
		isStopped = true;
		isCancelled = true;
	}

}
